// Fps Counter
// (c) jimon game studio

#include "CFpsCounter.h"
#include "ITimer.h"
#include "ILogManager.h"

namespace je
{
	namespace core
	{
		IMPLEMENT_SINGLETON(FpsCounter,JE_ENGINE_FUNCTION_CALL)

		//! Constructor
		CFpsCounter::CFpsCounter()
		:WorkMode(FCUM_USER),DeltaTime(1000.0),InvDeltaTime(1.0),Fps(0.0),ResetCounter(true),
		FrameDT(0),DTMode(DTCM_NONE),DefaultDTFps(60),DTMorphLevel(32),LastDTTime(0),ResetDT(true),EnableDT(true)
		{
			SetFpsCounter(this);
		}

		//! Destructor
		CFpsCounter::~CFpsCounter()
		{
			SetFpsCounter(NULL);
		}

		//! Return Current Fps
		f32 CFpsCounter::GetFps()
		{
			return Fps;
		}

		//! Return true if fps updated
		u1 CFpsCounter::IsUpdated()
		{
			return FpsUpdated;
		}

		//! Set Update Mode
		void CFpsCounter::SetUpdateMode(FpsCounterUpdateMode Mode,f32 NewDeltaTime)
		{
			if(!DeltaTime)
				DeltaTime = 1000.0;

			WorkMode = Mode;
			DeltaTime = NewDeltaTime;
			InvDeltaTime = (f32)(1000.0f / DeltaTime);
			ResetCounter = true;
		}

		//! Get Delta Time
		f64 CFpsCounter::GetDT()
		{
			if(!EnableDT)
				return 1.0f;

			switch(DTMode)
			{
				// TODO
			case DTCM_NICE:
				{
					JELOG_E("No Support")
					return FrameDT;
				}

			case DTCM_NONE:
			default:
				return FrameDT;
			}
		}

		//! Set Delta Time Correction Mode
		void CFpsCounter::SetDTCorrectionMode(DeltaTimeCorrectionMode Mode,u32 NewMorphLevel)
		{
			DTMode = Mode;
			DTMorphLevel = NewMorphLevel;
			ResetDT = true;
		}

		//! Set Delta Time Default FPS
		void CFpsCounter::SetDTDefaultFps(f32 Fps)
		{
			DefaultDTFps = Fps;
			ResetDT = true;
		}

		//! If false - disable dt, getdt will return 1.0
		void CFpsCounter::SetDTEnable(u1 Enable)
		{
			if(!EnableDT && Enable)
				ResetDT = true;

			EnableDT = Enable;

		}

		//! Loop Tick
		void CFpsCounter::LoopTick()
		{
			UpdateDT();

			switch(WorkMode)
			{
			case FCUM_USER:
				{
					FpsUpdated = false;

					if(ResetCounter)
					{
						ResetCounter = false;
						LastLoopTime = GetTimer()->GetTime();
						LoopCounts = 0;
						return;
					}

					LoopCounts++;

					f64 CurrentTime = GetTimer()->GetTime();
					if((CurrentTime - LastLoopTime) > DeltaTime)
					{
						LastLoopTime = CurrentTime;
						Fps = (f32)LoopCounts * InvDeltaTime;
						FpsUpdated = true;
						LoopCounts = 0;
					}
					break;
				}
			case FCUM_TICK:
				{
					if(ResetCounter)
					{
						ResetCounter = false;
						LastLoopTime = GetTimer()->GetTime();
						return;
					}

					f64 CurrentTime = GetTimer()->GetTime();

					if(CurrentTime == LastLoopTime)
						CurrentTime += 0.1f;

					Fps = (f32)(1000.0f / (CurrentTime - LastLoopTime));
					LastLoopTime = CurrentTime;
					FpsUpdated = true;

					break;
				}
			case FCUM_DOUBLE:
				{
					FpsUpdated = false;

					if(ResetCounter)
					{
						ResetCounter = false;
						LastLoopTime = GetTimer()->GetTime();
						TempLastLoopTime = LastLoopTime;
						LoopCounts = 0;
						TempFps = 0;
						return;
					}

					LoopCounts++;

					f64 CurrentTime = GetTimer()->GetTime();

					if(CurrentTime == TempLastLoopTime)
						CurrentTime += 0.1f;

					TempFps += (1000.0f / (CurrentTime - TempLastLoopTime));
					TempLastLoopTime = CurrentTime;

					if((CurrentTime - LastLoopTime) > DeltaTime)
					{
						LastLoopTime = CurrentTime;
						Fps = (f32)((f64)TempFps / (f64)LoopCounts);
						TempFps = 0;
						FpsUpdated = true;
						LoopCounts = 0;
					}

					break;
				}
			}
		}

		//! Update DeltaTime
		void CFpsCounter::UpdateDT()
		{
			if(!EnableDT)
				return;

			switch(DTMode)
			{
			case DTCM_NONE:
				{
					if(ResetDT)
					{
						LastDTTime = GetTimer()->GetTime();
						ResetDT = false;
					}
					else
					{
						f64 CurrentTime = GetTimer()->GetTime();
						f64 DeltaFrameTime = CurrentTime - LastDTTime;
						LastDTTime = CurrentTime;

						if(DeltaFrameTime == 0)
							DeltaFrameTime = 0.1f;

						FrameDT = ((f64)DefaultDTFps) * DeltaFrameTime / 1000.0f;
					}
					break;
				}
			case DTCM_NICE:
				{
					break;
				}
			}
		}
	}
}
